From a7ef3fe4f7f3702d8c8db4b78e5de91f4f38e03b Mon Sep 17 00:00:00 2001 From: joonhoekim <26rote@gmail.com> Date: Thu, 10 Jul 2025 09:52:56 +0000 Subject: (김준회) 나준규 프로 DB조회 우회 요청사항 (임시) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/[lng]/admin/temp-db-viewer/page.tsx | 141 ++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 app/[lng]/admin/temp-db-viewer/page.tsx (limited to 'app/[lng]/admin/temp-db-viewer/page.tsx') diff --git a/app/[lng]/admin/temp-db-viewer/page.tsx b/app/[lng]/admin/temp-db-viewer/page.tsx new file mode 100644 index 00000000..6692e63e --- /dev/null +++ b/app/[lng]/admin/temp-db-viewer/page.tsx @@ -0,0 +1,141 @@ +"use client"; + +import * as React from "react"; +import { useActionState, useState } from "react"; +import { executeSqlAction, type QueryResultState } from "./actions"; +import { Textarea } from "@/components/ui/textarea"; +import { Button } from "@/components/ui/button"; +import { toast } from "sonner"; + +// CSV 변환 유틸 +function convertToCSV(columns: string[], rows: Record[]): string { + const escape = (value: any) => { + if (value === null || value === undefined) return ""; + const str = String(value).replace(/"/g, '""'); + return `"${str}"`; + }; + + const header = columns.map(escape).join(","); + const lines = rows.map((row) => + columns.map((col) => escape(row[col])).join(",") + ); + return [header, ...lines].join("\r\n"); +} +// ──────────────────────────────────────────────────────────────────────────────── +// Main page component +// ──────────────────────────────────────────────────────────────────────────────── + +export default function SqlEditorPage() { + const [query, setQuery] = useState(""); + + const initialState: QueryResultState = { + columns: [], + rows: [], + }; + + // useActionState: 서버 액션과 클라이언트 상태 연결 + const [state, formAction, isPending] = useActionState< + QueryResultState, + FormData + >(executeSqlAction, initialState); + + // CSV 내보내기 핸들러 + const handleExportCSV = React.useCallback(() => { + if (state.rows.length === 0) { + toast.info("내보낼 결과가 없습니다."); + return; + } + + const csv = convertToCSV(state.columns, state.rows); + const blob = new Blob([csv], { type: "text/csv;charset=euc-kr;" }); + const url = URL.createObjectURL(blob); + const link = document.createElement("a"); + link.href = url; + link.download = "query_result.csv"; + link.click(); + URL.revokeObjectURL(url); + }, [state.columns, state.rows]); + + // 오류 toast 표시 + React.useEffect(() => { + if (state.error) { + toast.error(state.error); + } + }, [state.error]); + + return ( +
+ {/* 상단: 쿼리 입력 영역 */} +
+